package com.tyk.bookstore.back.product.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tyk.bookstore.back.common.core.util.AssertUtil;
import com.tyk.bookstore.back.common.core.util.IdGenerator;
import com.tyk.bookstore.back.common.core.util.ObjUtil;
import com.tyk.bookstore.back.common.core.util.TreeUtil;
import com.tyk.bookstore.back.product.model.dto.ClazzNestDto;
import com.tyk.bookstore.back.product.model.entity.BookDo;
import com.tyk.bookstore.back.product.model.entity.ClazzDo;
import com.tyk.bookstore.back.product.model.query.ClazzAddQuery;
import com.tyk.bookstore.back.product.model.query.ClazzListQuery;
import com.tyk.bookstore.back.product.model.query.ClazzPageQuery;
import com.tyk.bookstore.back.product.model.query.ClazzUpdateQuery;
import com.tyk.bookstore.back.product.model.result.ClazzResultEnum;
import com.tyk.bookstore.back.product.model.vo.ClazzListVo;
import com.tyk.bookstore.back.product.model.vo.ClazzNestVo;
import com.tyk.bookstore.back.product.model.vo.ClazzPageVo;
import com.tyk.bookstore.back.product.model.vo.ClazzVo;
import com.tyk.bookstore.back.product.repository.BookRepository;
import com.tyk.bookstore.back.product.repository.ClazzRepository;
import com.tyk.bookstore.back.product.service.ClazzService;
import com.tyk.bookstore.back.product.util.MapStructProduct;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;


/**
 * @author tyk
 * @description 分类
 */
@Service
@RequiredArgsConstructor

public class ClazzServiceImpl extends ServiceImpl<ClazzRepository, ClazzDo> implements ClazzService {

    private final ClazzRepository clazzRepository;
    private final BookRepository bookRepository;
    private final MapStructProduct mapStructProduct;

    @Override
    public ClazzVo getById(Long id) {
        ClazzDo cd = clazzRepository.selectById(id);
        AssertUtil.notNull(cd, ClazzResultEnum.CLAZZ_NOT_FOUND);
        return mapStructProduct.toClassificationVo(cd);
    }

    @Override
    public List<ClazzNestVo> getNestList() {
        // 查询分类列表
        List<ClazzNestDto> dtoList = clazzRepository.selectClazzNestDtoList();
        // 构建树型结构并返回
        return TreeUtil.buildTree(
                dtoList,
                ClazzNestDto::getId,
                ClazzNestDto::getParentId,
                mapStructProduct::toClazzNestVo,
                ClazzNestVo::addChild
        );
    }

    @Override
    public List<ClazzListVo> getList(ClazzListQuery query) {
        LambdaQueryWrapper<ClazzDo> w = new LambdaQueryWrapper<>();
        w.eq(query.getLevel() != null, ClazzDo::getLevel, query.getLevel());
        w.eq(query.getParentId() != null, ClazzDo::getParentId, query.getParentId());
        w.like(ObjUtil.notBlank(query.getName()), ClazzDo::getName, query.getName());
        List<ClazzDo> doList = clazzRepository.selectList(w);
        return mapStructProduct.toClazzListVo(doList);
    }

    @Override
    public ClazzPageVo getPage(ClazzPageQuery query) {
        Page<ClazzDo> page = new Page<>(query.getPage(), query.getLimit());
        LambdaQueryWrapper<ClazzDo> w = new LambdaQueryWrapper<>();
        w.eq(query.getLevel() != null, ClazzDo::getLevel, query.getLevel());
        w.eq(query.getParentId() != null, ClazzDo::getParentId, query.getParentId());
        w.like(ObjUtil.notBlank(query.getName()), ClazzDo::getName, query.getName());
        w.orderByDesc(ClazzDo::getCreateTime);
        clazzRepository.selectPage(page, w);
        return mapStructProduct.toClassificationPageVo(page);
    }

    @Override
    public void add(ClazzAddQuery query) {

        // 校验父级存在
        Byte level = checkParentClazz(query.getParentId());

        // 父级下不重名
        AssertUtil.isFalse(
                clazzRepository.exist2Eq(
                        ClazzDo::getParentId, query.getParentId(),
                        ClazzDo::getName, query.getName(),
                        ClazzDo.class
                ),
                ClazzResultEnum.CLAZZ_ALREADY_EXISTS
        );

        // 添加到数据库
        ClazzDo cd = mapStructProduct.toClassificationDo(query);
        cd.setLevel((byte) (level + 1));
        cd.setId(IdGenerator.id());
        clazzRepository.insert(cd);
    }

    @Override
    public void updateById(ClazzUpdateQuery query) {

        // 分类存在
        AssertUtil.isTrue(
                clazzRepository.exist1Eq(
                        ClazzDo::getId, query.getId(), ClazzDo.class
                ),
                ClazzResultEnum.CLAZZ_NOT_FOUND
        );

        // 检查父级分类
        Byte level = checkParentClazz(query.getParentId());

        // 父级下不重名
        AssertUtil.isFalse(
                clazzRepository.exist2Eq1Ne(
                        ClazzDo::getParentId, query.getParentId(),
                        ClazzDo::getName, query.getName(),
                        ClazzDo::getId, query.getId(),
                        ClazzDo.class
                ),
                ClazzResultEnum.CLAZZ_ALREADY_EXISTS
        );

        // 更新
        ClazzDo cd = mapStructProduct.toClassificationDo(query);
        cd.setLevel((byte) (level + 1));
        clazzRepository.updateById(cd);

    }


    @Override
    public void deleteById(Long id) {
        // 分类存在
        AssertUtil.isTrue(
                clazzRepository.exist1Eq(
                        ClazzDo::getId, id, ClazzDo.class
                ),
                ClazzResultEnum.CLAZZ_NOT_FOUND
        );
        // 分类下不存在书籍
        AssertUtil.isFalse(
                bookRepository.exist1Eq(
                        BookDo::getClazzId, id, BookDo.class
                ),
                ClazzResultEnum.CLAZZ_HAS_BOOK
        );
        // 分类不存在下级
        AssertUtil.isFalse(
                clazzRepository.exist1Eq(
                        ClazzDo::getParentId, id, ClazzDo.class
                ),
                ClazzResultEnum.CLAZZ_HAS_CHILD
        );
        // 删除分类
        clazzRepository.deleteById(id);
    }

    @Override
    public void deleteBatchByIdSet(Set<Long> idSet) {
        // 分类存在
        AssertUtil.equal(
                clazzRepository.count1In(
                        ClazzDo::getId, idSet, ClazzDo.class
                ),
                (long) idSet.size(),
                ClazzResultEnum.CLAZZ_NOT_FOUND
        );
        // 分类下不存在书籍
        AssertUtil.equal(
                bookRepository.count1In(
                        BookDo::getClazzId, idSet, BookDo.class
                ),
                0L,
                ClazzResultEnum.CLAZZ_HAS_BOOK
        );
        // 删除
        clazzRepository.deleteBatchIds(idSet);
    }


    /**
     * 校验父级分类
     *
     * @param parentId 父级分类ID
     * @return 父级级别
     */
    private Byte checkParentClazz(Long parentId) {
        // 父级存在
        if (!ObjUtil.equal(parentId, ClazzDo.TOP_ID)) {
            Byte level = clazzRepository.<Integer>selectSin1Eq(
                    ClazzDo::getLevel,
                    ClazzDo::getId, parentId,
                    ClazzDo.class
            ).getV1().byteValue();
            AssertUtil.notNull(level, ClazzResultEnum.CLAZZ_PARENT_NOT_FOUND);
            // 父级不存在书籍
            AssertUtil.isFalse(
                    bookRepository.exist1Eq(
                            BookDo::getClazzId, parentId, BookDo.class
                    ),
                    ClazzResultEnum.CLAZZ_PARENT_HAS_BOOK
            );
            return level;
        }
        return 1;
    }

}

